package cn.lanqiao.springbootproject.filter;

import cn.lanqiao.springbootproject.util.CurrentUserUtil;
import cn.lanqiao.springbootproject.util.Result;
import cn.lanqiao.springbootproject.vo.UserRoleVO;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

@Slf4j
//@Configuration
//@Order(2)
//@WebFilter(urlPatterns = "/user/*")
public class LoginFilter implements Filter {


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //准备对象
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        log.info(request.getMethod());

        // 检查请求的URL是否在排除列表中
        String path = "/user/login,/user/register,/user/getCode,/user/getPhoneCode/1234567890,/user/login2";
        List<String> excludedUrls = Arrays.asList(path.split(","));
        if (excludedUrls.contains(request.getRequestURI())) {
            filterChain.doFilter(servletRequest, servletResponse); // 不拦截，继续请求
            return;
        }

        try {
            /**
             * 认证操作
             */
            String token = request.getHeader("Authorization");
            if (token == null) {
                failResult(603, "请先登录！！", response);
                return;
            }
            //解密操作
            Algorithm algorithm = Algorithm.HMAC256("david");//设置加密方式
            JWTVerifier jwtVerifier = JWT.require(algorithm).build();//创建认证对象，创建失败会抛出AlgorithmMismatchException异常。
            DecodedJWT decodedJWT = jwtVerifier.verify(token.substring(7));//对去掉头信息的token进行验证

            /**
             * 鉴权操作
             */
            //获得JWT信息
            UserRoleVO userRoleVO = new UserRoleVO();
            userRoleVO.setUserId(decodedJWT.getClaim("userId").asInt());
            userRoleVO.setUserName(decodedJWT.getClaim("userName").asString());
            userRoleVO.setRoleId(decodedJWT.getClaim("roleId").asInt());
            userRoleVO.setRoleName(decodedJWT.getClaim("roleName").asString());
            //menu鉴权-RBAC项目用
            CurrentUserUtil.set(userRoleVO);
            /*
            CRUD鉴权-扩展知识点，RBAC项目不用
            table：roleId | model | power | comm
                    1       user   delete       用户模块的添加用户
                    2       user   query
            */
            //使用roleId获得model和power  url = /user/delete
            // List urlList = mapper.getModelAndPower(roleId)
            // if(!urlList.contains(request.getRequestURL())) {
            //   failResult(604, "该用户无权访问此功能！", response);
            //   return;
            //}


        } catch (JWTDecodeException e) {
            failResult(604, "秘钥格式错误！", response);
            return;
        } catch (SignatureVerificationException e) {
            failResult(605, "秘钥解密错误！", response);
            return;
        } catch (TokenExpiredException e) {
            failResult(606, "token过期，请重新登录！", response);
            return;
        } catch (Exception e) {
            e.printStackTrace();
        }
        //调用下一个 filter 或者 servlet
        filterChain.doFilter(servletRequest, servletResponse);

    }

    /**
     * 验证失败统一向前端返回错误结果
     *
     * @param code     错误码
     * @param message  错误信息
     * @param response 响应对象
     */
    public boolean failResult(int code, String message, HttpServletResponse response) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();//使用jackson，也可以使用fastjson
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().print(objectMapper.writeValueAsString(Result.error(code, message)));
            response.flushBuffer();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

}
